/*
 * ConnectionManager.cpp
 *
 *  Created on: Nov 21, 2014
 *      Author: toti
 */

#include <Connectivity/ConnectionManager.hpp>

ConnectionManager::ConnectionManager() {

	inboundMessagePackageList = new std::list<MessagePackage*>();
	outboundMessagePackageList = new std::list<MessagePackage*>();

	parentServer = 0;
	localServerList = new std::list<LocalNode*>();
	localClientList = new std::list<LocalNode*>();

	localClientTcpServer = 0;
	localServerTcpServer = 0;

	epoll = new EPoll(32);

}

void ConnectionManager::initParentServer() {

	TCPClient parentServerClient(Config::instance()->isIPv6Flag());
	TCPConnection *parentServerTcpConnection;
	LocalServer *localServer;
	ServerMessagePackage *serverMessagePackage;

	parentServerTcpConnection = parentServerClient.openConnection(
			Config::instance()->getParentServerPort(),
			Config::instance()->getAddress());

	localServer = new LocalServer(parentServerTcpConnection);

	parentServer = localServer;

	epoll->addFd(parentServerTcpConnection->getSock(), localServer);

	serverMessagePackage = new ServerMessagePackage(localServer);
	serverMessagePackage->setMessageType(ServerMessagePackage::TYPE_SERVER_REGISTRATION);
	serverMessagePackage->setSender(Config::instance()->getServerName());
	serverMessagePackage->setParameter(ServerMessagePackage::SERVER_REGISTRATION_REQUEST_SERVERLIST, "true");
	serverMessagePackage->generateMessageString();

	outboundMessagePackageList->push_back((MessagePackage*)serverMessagePackage);

}

void ConnectionManager::initChildServer() {

	localServerTcpServer = new TCPServer(
			Config::instance()->getChildServerPort(), Config::instance()->isIPv6Flag());
	localClientTcpServer = new TCPServer(
			Config::instance()->getChildClientPort(), Config::instance()->isIPv6Flag());

	epoll->addFd(localServerTcpServer->getSock(), localServerTcpServer);
	epoll->addFd(localClientTcpServer->getSock(), localClientTcpServer);

}

void ConnectionManager::stopServerListening(){

	if(localServerTcpServer->isServerOpen()){
		localServerTcpServer->closeServer();
	}

}

void ConnectionManager::handleInputData() {

	int n = epoll->wait();

	for (int i = 0; i < n; i++) {

		struct epoll_event *event = epoll->getEventBuffer(i);

		if ((event->events & EPOLLERR) || (event->events & EPOLLHUP)
				|| (event->events & EPOLLRDHUP)) {

			handleExistingLocalNodeError((LocalNode*)event->data.ptr);

		} else if (event->events & EPOLLIN) {

			if (event->data.ptr == localClientTcpServer) {

				handleNewLocalClientConnections((TCPServer*)localClientTcpServer);

			} else if (event->data.ptr == localServerTcpServer) {

				handleNewLocalServerConnections((TCPServer*)localServerTcpServer);

			} else {

				handleExistingLocalNodeConnection((LocalNode*)event->data.ptr);

			}

		}

	}

}

void ConnectionManager::handleOutputData() {


	while(outboundMessagePackageList->size() > 0){

		MessagePackage *messagePackage = outboundMessagePackageList->front();
		outboundMessagePackageList->pop_front();

		LocalNode *localNode = messagePackage->getLocalNode();

		if(localNode){
			messagePackage->generateMessageString();

			localNode->handleOutputData(messagePackage);

		}

		delete(messagePackage);

	}

}



void ConnectionManager::handleExistingLocalNodeError(LocalNode *localNode){

	ClientMessagePackage *clientMessagePackage = 0;

	switch(localNode->getLocalNodeType()){

	case LocalNode::LOCAL_NODE_CLIENT:

		clientMessagePackage = new ClientMessagePackage(localNode);
		clientMessagePackage->setMessageType(ClientMessagePackage::TYPE_QUIT_MESSAGE);
		inboundMessagePackageList->push_back(clientMessagePackage);

		break;
	case LocalNode::LOCAL_NODE_SERVER:

		localServerList->remove(localNode);
		delete(localNode);

		break;

	}
}


void ConnectionManager::handleNewLocalClientConnections(TCPServer *tcpServer) {

	TCPConnection *tcpConnection = localClientTcpServer->waitForConnection();
	LocalClient *localClient = new LocalClient(tcpConnection);

	localClientList->push_back(localClient);
	epoll->addFd(tcpConnection->getSock(), localClient);

	ClientMessagePackage *clientMessagePackage = new ClientMessagePackage(localClient);
	clientMessagePackage->setMessageType(ClientMessagePackage::TYPE_REGISTRATION);

	inboundMessagePackageList->push_back((MessagePackage*)clientMessagePackage);

}

void ConnectionManager::handleNewLocalServerConnections(TCPServer *tcpServer) {

	TCPConnection *tcpConnection = localServerTcpServer->waitForConnection();
	LocalServer *localServer = new LocalServer(tcpConnection);

	epoll->addFd(tcpConnection->getSock(), localServer);
	localServerList->push_back(localServer);

}

void ConnectionManager::handleExistingLocalNodeConnection(LocalNode *localNode){


	if(!localNode->handleInputData(inboundMessagePackageList)){

		handleExistingLocalNodeError(localNode);

	}

}

LocalNode *ConnectionManager::getLocalParentServer(){
	return parentServer;
}

std::list<LocalNode*> *ConnectionManager::getLocalClientList(){
	return localClientList;
}

std::list<LocalNode*> *ConnectionManager::getLocalServerList(){
	return localServerList;
}


void ConnectionManager::pushOutboundMessagePackageList(MessagePackage *messagePackage){
	outboundMessagePackageList->push_back(messagePackage);
}


MessagePackage *ConnectionManager::popInboundMessagePackageList(){

	MessagePackage *messagePackage = 0;

	if(inboundMessagePackageList->size()){
		messagePackage = inboundMessagePackageList->front();
		inboundMessagePackageList->pop_front();
	}

	return messagePackage;
}

